package com.java.jvalidator.framework;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ErrorMsg {
	// 消息等资源国际化用的xml文件名,例如：jValidator_Message_en.properties、jValidator_Message_zh.properties
	private static final String RESOURCE_BASE_NAME = "jValidator_Message";

	private static Pattern ID_REPLACE_PATTERN = Pattern.compile("\\{#[a-zA-Z0-9_]+\\}");
	private static Pattern PROPERTY_VALUE_REPLACE_PATTERN = Pattern.compile("\\{@([a-zA-Z_][\\w.]+|~)\\}");
	private static Pattern PROPERTY_NAME_REPLACE_PATTERN = Pattern.compile("\\{\\$([a-zA-Z_][\\w.]+|~)\\}");
	private static Pattern NAMED_PLACE_HOLDER_PATTERN = Pattern.compile("\\{[a-zA-Z_][\\w]+}");

	private static String DEFAULT_SIGH = "~";
	private static final String DOT_REGEX = ".";
	private static IPropertyNameRetriever retriever = new PropertyNameRetriever();
	private String errorMsgId;
	private Object validateTarget;
	private String propertyName;
	private String[] placeHolderValue;
	private Map<String, String> namedPlaceHolder;
	private Locale locale;

	/**
	 * ErrorMsg构造函数
	 * 
	 * @param validateTarget
	 *            出错的对象
	 * @param property
	 *            出错的字段名
	 * @param errorMsgName
	 *            错误消息id
	 */
	public ErrorMsg(Object validateTarget, String property, String errorMsgName) {
		if (property.contains(DOT_REGEX)) {
			property = property.substring(property.lastIndexOf(DOT_REGEX) + 1);
		}
		this.errorMsgId = errorMsgName;
		this.validateTarget = validateTarget;
		this.propertyName = property;
		this.namedPlaceHolder = new HashMap<String, String>();
	}

	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}

		if (this == obj) {
			return true;
		}

		if (this.getClass() != obj.getClass()) {
			return false;
		}

		ErrorMsg other = (ErrorMsg) obj;
		// errorMsgId
		if (this.errorMsgId == null) {
			if (other.errorMsgId != null) {
				return false;
			}
		} else if (!this.errorMsgId.equals(other.errorMsgId)) {
			return false;
		}

		// locale
		if (this.locale == null) {
			if (other.locale != null) {
				return false;
			}
		} else if (!this.locale.equals(other.locale)) {
			return false;
		}

		// namedPlaceHolder
		if (this.namedPlaceHolder == null) {
			if (other.namedPlaceHolder != null) {
				return false;
			}
		} else if (!this.namedPlaceHolder.equals(other.namedPlaceHolder)) {
			return false;
		}

		// placeHolderValue
		if (!Arrays.equals(this.placeHolderValue, other.placeHolderValue)) {
			return false;
		}

		// propertyName
		if (this.propertyName == null) {
			if (other.propertyName != null) {
				return false;
			}
		} else if (!this.propertyName.equals(other.propertyName)) {
			return false;
		}

		// validateTarget
		if (this.validateTarget == null) {
			if (other.validateTarget != null) {
				return false;
			}
		} else if (!this.validateTarget.equals(other.validateTarget)) {
			return false;
		}

		return true;
	}

	public Map<String, String> getNamedPlaceHolder() {
		return this.namedPlaceHolder;
	}

	public void setNamedPlaceHolder(Map<String, String> namedPlaceHolder) {
		this.namedPlaceHolder = namedPlaceHolder;
	}

	public String getErrorMsgId() {
		return this.errorMsgId;
	}

	public String getPropertyName() {
		return this.propertyName;
	}

	public String getMessage() throws ValidationException {
		return getMessage(null);
	}

	/****
	 * 取得Message（国际化后的）文本
	 * 
	 * @param locale
	 *            如果为null,则默认为Locale.getDefault()
	 * @return
	 * @throws ValidationException
	 */
	public String getMessage(Locale locale) throws ValidationException {
		if (locale == null) {
			return getMessage(Locale.getDefault());
		}
		this.locale = locale;
		ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BASE_NAME, locale);
		String msg = null;
		try {
			msg = bundle.getString(TypeUtil.trimString(this.errorMsgId));
			msg = processNamedPlaceHolder(msg);
			msg = processExpressions(bundle, msg);
			msg = MessageFormat.format(msg, new Object[] { this.placeHolderValue });
			return processExpressions(bundle, msg);
		} catch (MissingResourceException e) {
			throw new ValidationException(e.getMessage());
		} catch (IllegalArgumentException e) {
			throw new ValidationException(e.getMessage());
		}
	}

	private String processNamedPlaceHolder(String msg) {
		if ((msg == null) || (this.namedPlaceHolder == null)) {
			return null;
		}
		Matcher holderMatcher = NAMED_PLACE_HOLDER_PATTERN.matcher(msg);
		String result = msg;
		while (holderMatcher.find()) {
			String expression = holderMatcher.group();
			String name = expression.substring(1, expression.length() - 1);
			String value = (String) this.namedPlaceHolder.get(name);
			if (!TypeUtil.isStringNullOrEmpty(value)) {
				result = result.replace(expression, value);
			}
		}
		return result;
	}

	public Object getValidateTarget() {
		return this.validateTarget;
	}

	public int hashCode() {
		int prime = 31;
		int result = 1;
		result = prime * result + (this.errorMsgId == null ? 0 : this.errorMsgId.hashCode());
		result = prime * result + (this.locale == null ? 0 : this.locale.hashCode());
		result = prime * result + (this.namedPlaceHolder == null ? 0 : this.namedPlaceHolder.hashCode());
		result = prime * result + Arrays.hashCode(this.placeHolderValue);
		result = prime * result + (this.propertyName == null ? 0 : this.propertyName.hashCode());
		result = prime * result + (this.validateTarget == null ? 0 : this.validateTarget.hashCode());
		return result;
	}

	public void setPlaceHolderValue(String[] strings) {
		this.placeHolderValue = strings;
	}

	private String getExpressionKey(String expression) throws ValidationException {
		if ((expression == null) || (expression.length() < 3)) {
			throw new ValidationException(ValidationExceptionMsg.ERROR_MSG_EXPRESSION_ERROR + expression);
		}
		return expression.substring(2, expression.length() - 1);
	}

	private Object getPropertyValue(Object target, String propertyName) throws ValidationException {
		if ((target == null) || (TypeUtil.isStringNullOrEmpty(propertyName))) {
			throw new ValidationException(ValidationExceptionMsg.VALIDATION_ERROR);
		}

		String[] properties = propertyName.trim().split(DOT_REGEX);
		Object propertyTarget = target;
		Object propertyValue = null;

		for (int i = 0; i < properties.length; i++) {
			String property = properties[i];
			propertyValue = TypeUtil.getPropertyValue(propertyTarget, property);
			if ((propertyValue == null) && (i < properties.length - 1)) {
				throw new ValidationException(ValidationExceptionMsg.VALIDATION_ERROR);
			}
			propertyTarget = propertyValue;
		}
		return propertyValue;
	}

	private String getValueFromBundle(ResourceBundle bundle, String key) throws ValidationException {
		if ((bundle == null) || (key == null))
			return "";
		try {
			return bundle.getString(key);
		} catch (MissingResourceException e) {
			throw new ValidationException(e.getMessage());
		}
	}

	private String idReplace(ResourceBundle bundle, String content) throws ValidationException {
		Matcher idHolderMatcher = ID_REPLACE_PATTERN.matcher(content);
		String result = content;
		while (idHolderMatcher.find()) {
			String expression = idHolderMatcher.group();
			String key = getExpressionKey(expression);
			String value = getValueFromBundle(bundle, key);
			value = idReplace(bundle, value);
			result = result.replace(expression, value);
		}
		return result;
	}

	private String processExpressions(ResourceBundle bundle, String content) throws ValidationException {
		String result = content;
		result = idReplace(bundle, result);

		result = propertyNameReplace(result);
		result = propertyValueReplace(bundle, result);
		return result;
	}

	private String propertyNameReplace(String result) throws ValidationException {
		if ((retriever == null) || (this.validateTarget == null) || (this.propertyName == null) || (result == null)) {
			return result;
		}

		Matcher properNameMatcher = PROPERTY_NAME_REPLACE_PATTERN.matcher(result);
		while (properNameMatcher.find()) {
			String expression = properNameMatcher.group();
			String propName = getExpressionKey(expression);

			if (DEFAULT_SIGH.equals(propName)) {
				propName = this.propertyName;
			}
			String name = retriever.retrieveName(this.validateTarget, propName, this.locale);
			result = result.replace(expression, name);
		}
		return result;
	}

	private String propertyValueReplace(ResourceBundle bundle, String value) throws ValidationException {
		Matcher propHodlerMatcher = PROPERTY_VALUE_REPLACE_PATTERN.matcher(value);
		String result = value;
		while (propHodlerMatcher.find()) {
			String expression = propHodlerMatcher.group();
			String propertyName = getExpressionKey(expression);

			if (DEFAULT_SIGH.equals(propertyName)) {
				propertyName = this.propertyName;
			}
			Object propValue = getPropertyValue(this.validateTarget, propertyName);
			if (propValue == null) {
				throw new ValidationException(ValidationExceptionMsg.FIELD_CONFIG_EMPTY_ERROR);
			}
			result = result.replace(expression, propValue.toString());
		}
		return result;
	}
}